﻿#include "lang_impl.hh"
#include "../util/string_util.hh"
#include "../box/service_box.hh"
#include <fstream>

namespace kratos {
namespace lang {

#define LANG_DEFINE(name, s)\
   {std::underlying_type<LangID>::type(LangID::name), {s, ""} },

#define LANG_DEFINE_TOKEN(name, s, t)\
    {std::underlying_type<LangID>::type(LangID::name), {s, t} },

static DefaultMap global_default_lang = {
    LANG_DEFINE(LANG_BOX_PARSE_ARGUMENT_FAIL, "[box]启动参数错误")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_CONFIG_FAIL, "[box][config]配置文件加载失败'%s'", "%s")
    LANG_DEFINE(LANG_BOX_OS_REGISTER_FAIL, "[box]操作系统平台注册失败")
    LANG_DEFINE(LANG_BOX_START_NETWORK_FAIL, "[box]启动网络组件失败")
    LANG_DEFINE(LANG_BOX_GETTING_NECESSARY_SERVICE, "[box]正在获取必需的服务")
    LANG_DEFINE(LANG_BOX_GOT_NECESSARY_SERVICE, "[box]必需的服务获取完成")
    LANG_DEFINE(LANG_BOX_STARTED, "[box]服务容器启动")
    LANG_DEFINE(LANG_BOX_START_FAILURE, "[box]服务容器启动失败")
    LANG_DEFINE_TOKEN(LANG_BOX_PARSE_CONFIG_FAIL, "[box]配置文件错误:'%s'", "%s")
    LANG_DEFINE_TOKEN(LANG_BOX_CREATE_FINDER_FAIL, "[box]服务器发现组件错误，发现服务器类型'%s'", "%s")
    LANG_DEFINE_TOKEN(LANG_BOX_START_FINDER_FAIL, "[box]服务注册组件错误，发现服务器类型'%s', 发现服务地址'%s'", "%s %s")
    LANG_DEFINE(LANG_BOX_START_CLEANUP, "[box]服务容器清理")
    LANG_DEFINE(LANG_BOX_STOPPED, "[box]服务容器关闭")
    LANG_DEFINE_TOKEN(LANG_BOX_SERVICE_ADDRESS_INCORRECT, "[box]服务地址格式错误'%s', 服务名'%s'", "%s %s")
    LANG_DEFINE(LANG_BOX_CREATE_LOGGER_FAIL, "[box]创建日志组件失败")
    LANG_DEFINE_TOKEN(LANG_BOX_START_LOGGER_FAIL, "[box]启动日志添加器失败, 日志配置'%s', 错误'%s'", "%s %s")
    LANG_DEFINE_TOKEN(LANG_BOX_LOCAL_SERVICE_DIR_NOT_FOUND, "[box]服务目录不存在:%s", "%s")
    LANG_DEFINE(LANG_BOX_START_REMOTE_SERVICE_FAIL, "[box]服务更新组件启动失败")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_LOCAL_SERVICE_FAIL, "[box]加载服务'%s'失败, 路径'%s', 服务标识'%s'", "%s %s %s")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_LOCAL_SERVICE_DIR_NOT_FOUND, "[box]加载服务'%s'失败, 路径不存在, 路径'%s', 服务标识'%s'", "%s %s %s")
    LANG_DEFINE_TOKEN(LANG_BOX_LOAD_LOCAL_SERVICE_SUCCESS, "[box]启动本地服务:%s", "%s")
    LANG_DEFINE_TOKEN(LANG_BOX_START_LISTENER_FAIL, "[box]开启容器监听失败，地址'%s'", "%s")
    LANG_DEFINE_TOKEN(LANG_OS_RELOAD_CONFIG_FAIL, "[box]重新加载配置失败，%s, 错误: %s", "%s %s")
    LANG_DEFINE_TOKEN(LANG_OS_RELOAD_CONFIG_SUCCESS, "[box]重新加载配置成功，%s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CONNECT_REMOTE_REPO_FAIL, "[box]连接远程服务仓库失败，调用API：%s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_PARSE_REMOTE_VERSION_CONFIG_FAIL, "[box]解析远程版本配置失败, API: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LOCAL_FILE_FAIL, "[box]创建本地文件失败, API：%s, 本地路径: %s", "%s %s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LOCAL_DIR_FAIL, "[box]建立临时服务版本目录失败，目录名：%s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_DOWNLOAD_REMOTE_BUNDLE_FAIL, "[box]下载远程服务失败，API：%s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_UPDATE_REMOTE_SERVICE_EXCEPT, "[box]更新远程服务发生异常: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_UPDATE_SERVICE_FAIL, "[box]更新服务失败，新服务：%s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LOCAL_VERSION_FILE_FAIL, "[box]创建版本文件失败，版本号：%s", "%s")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_CONNECTOR_FAIL, "[box]启动连接器失败 [%s:%d]", "%s %d")
    LANG_DEFINE_TOKEN(LANG_HTTP_CREATE_LISTENER_FAIL, "[box]启动监听器失败 [%s:%d]", "%s %d")
    LANG_DEFINE(LANG_SERVICE_CONTEXT_REQUEST_SHUTDOWN, "[box]请求关闭服务容器")
    LANG_DEFINE_TOKEN(LANG_CORO_RUNNER_EXCEPTION, "[box]协程运行时抛出异常，原因: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_COMMAND_EXCEPTION, "[box]执行命令发生异常，命令：%s, 异常：%s", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_LISTENER,"[box]监听本地地址: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_FINDER, "[box]服务发现类型: %s, 地址: %s", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_NECESSARY_SERVICE, "[box]获取服务: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REGISTER_SERVICE, "[box]注册服务: %s, 监听地址: %s", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_UNREGISTER_SERVICE, "[box]取消注册服务: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_COROUTINE, "[box]开启协程模式: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_LOCAL_SERVICE_DIR, "[box]本地服务目录: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_LOCAL_SERVICE, "[box]预加载的本地服务: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_OPEN_REMOTE_UPDATE, "[box]开启远程更新: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_REPO_DIR, "[box]远程仓库地址: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_VERSION_API, "[box]远程仓库版本调用接口: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_LATEST_VRSION_API, "[box]远程仓库最新版本调用接口: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_REMOTE_UPDATE_CHECK_INTVAL, "[box]远程服务版本检查周期: %d(秒)", "%d")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_DAEMON, "[box]守护进程模式: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_COMMAND_MANAGER, "[box]命令管理器开启: %s","%s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_INFO_COMMAND_MANAGER_ADDRESS, "[box]命令管理器监听地址: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_COMMAND_INFO, "[box]执行外部命令: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_RELOAD_INFO, "[box]重新加载配置, 旧配置: %s, 新配置: %s", "%s %s")
    LANG_DEFINE_TOKEN(LANG_STARTUP_EXCEPTION, "[box]启动遇到未知错误: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_RUNTIME_EXCEPTION, "[box]运行期间遇到未知错误: 异常[%s], %s, 继续运行可能会遇到无法预期的错误", "%s %s")
    LANG_DEFINE_TOKEN(LANG_START_LISTENER_FAIL, "[box]启动容器监听器失败: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_CONFIG_ERROR, "[box]配置文件错误, 属性: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_UNEXPECTED_EXCEPTION, "[box][%s]未预期的异常: 类型[%s]%s", "%s %s %s")
    LANG_DEFINE_TOKEN(LANG_UNEXPECTED_ERROR, "[box][%s]未预期的错误: %s", "%s %s")
    LANG_DEFINE(LANG_PROXY_CONFIG_NOT_FOUND, "[box][proxy]代理监听配置未找到: proxy.listener")
    LANG_DEFINE(LANG_START_AS_PROXY, "[box][proxy]以代理模式启动")
    LANG_DEFINE(LANG_PROXY_SEED_NOT_FOUND, "[box][proxy]代理种子配置未找到: proxy.seed")
    LANG_DEFINE_TOKEN(LANG_REGISTER_UUID_SERVICE_FAILED, "[box]注册服务失败, UUID[%s],service[%s]", "%s %s")
    LANG_DEFINE(LANG_PROXY_VIRTUAL_ID_EXHAUSTED, "[box][proxy]代理虚拟ID空间耗尽")
    LANG_DEFINE(LANG_PROXY_ADD_OUTSIDE_TRANSPORT_FAILED, "[box][proxy]添加外部连接管道失败")
    LANG_DEFINE_TOKEN(LANG_PROXY_FIND_SERVICE_TIMEOUT, "[box][proxy]发现服务超时，UUID[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_PROXY_INVALID_HEADER, "[box][proxy]协议头格式错误, 协议头[%s]", "%s")
    LANG_DEFINE(LANG_PROXY_NOT_FOUND_SERVICE_FOR_RETURN, "[box][proxy]外部调用返回但未找到内部服务")
    LANG_DEFINE_TOKEN(LANG_PROXY_CONFIG_ERROR, "[box][proxy]代理配置错误: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_PROXY_LISTENER_INFO, "[box][proxy]代理监听端口: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_REDIS_INVALID_ARGUMENT, "[box][redis]参数错误: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_REDIS_CONNECT_FAIL, "[box][redis]连接主机失败: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_REDIS_EXEC_FAIL, "[box][redis][worker]执行命令失败: %s", "%s")
    LANG_DEFINE_TOKEN(LANG_HOST_CONNECT_INFO, "[box][service_layer]连接到服务容器, 服务[%s], 地址[%s], 服务引用[%d]", "%s %s %d")
    LANG_DEFINE_TOKEN(LANG_HOST_DISCONNECT_INFO, "[box][service_layer]服务容器连接断开, 服务[%s], 地址[%s], 服务引用[%d]", "%s %s %d")
    LANG_DEFINE_TOKEN(LANG_TIMER_HANDLE_EXCEPTION, "[box][timer]定时器处理器抛出异常, 原因[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CSV_REBUILD_INDEX_FAIL, "[box][csv]热更新后重建索引失败，文件[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CONFIG_TYPE_ERROR, "[box][config]配置类型错误, 属性[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_CONFIG_MISSING_ATTR, "[box][config]配置属性未找到，属性[%s]", "%s")
    LANG_DEFINE_TOKEN(LANG_ASSERTION_FAIL, "[box]断言失败[%s], 位置[%s:%d]:\n堆栈:%s", "%s %s %d %s")
    LANG_DEFINE_TOKEN(LANG_SERVICE_ASSERTION_FAIL, "断言失败[%s], 位置[%s:%d]:\n堆栈:%s", "%s %s %d %s")
    LANG_DEFINE(LANG_CONSOLE_CONFIG_ERROR, "[box][console]配置错误, 未找到页面文件配置")
    LANG_DEFINE_TOKEN(LANG_LUA_ERROR, "[box][lua]%s", "%s")
};

kratos::lang::LangImpl::LangImpl(bool internal) {
  if (internal) {
    load_default("cn-simple", global_default_lang);
  }
}

kratos::lang::LangImpl::~LangImpl() {}

auto kratos::lang::LangImpl::load_default(const std::string &default_lang_path)
    -> bool {
  default_lang_map_.clear();
  if (!load(default_lang_path)) {
    return false;
  }
  default_lang_map_.swap(lang_map_);
  language_.clear();
  return true;
}

auto kratos::lang::LangImpl::load_default(const std::string& name,
                                          const DefaultMap &default_map)
    -> void {
  default_language_ = name;
  for (const auto& [k, v] : default_map) {
      default_lang_map_[k] = v;
  }
}

auto kratos::lang::LangImpl::get_language() -> const std::string & {
  if (language_.empty()) {
    return default_language_;
  }
  return language_;
}

auto kratos::lang::LangImpl::get_lang(kratos::lang::LangID id) noexcept
    -> const std::string & {
  static std::string null;
  auto it = lang_map_.find(static_cast<std::uint64_t>(id));
  if (it == lang_map_.end()) {
    auto it_default = default_lang_map_.find(static_cast<std::uint64_t>(id));
    if (it_default != default_lang_map_.end()) {
      return it_default->second.line;
    } else {
      return null;
    }
  }
  return it->second.line;
}

auto kratos::lang::LangImpl::load(const std::string &file_path) -> bool {
  std::ifstream ifs;
  ifs.open(file_path, std::ios::in);
  if (!ifs) {
    return false;
  }
  std::string line;
  std::getline(ifs, line);
  std::vector<std::string> result;
  util::split(line, " ", result);
  if (result.size() != 2) {
    return false;
  }
  std::string language = result[1];
  std::vector<std::string> token_result;
  while (std::getline(ifs, line)) {
    if (line.empty()) {
      continue;
    }
    std::string log_line;
    util::splitBy(line, ' ', result);
    if (result.size() < 2) {
      lang_map_.clear();
      return false;
    }
    if (result.size() >= 2) {
      for (std::size_t i = 1; i < result.size(); i++) {
        log_line += result[i];
      }
    }
    std::uint64_t lang_id = std::stoull(result[0]);
    lang_map_[lang_id] = { log_line, "" };
    auto pos = log_line.find('%');
    auto it = global_default_lang.find(lang_id);
    if (it == global_default_lang.end()) {
      return false;
    }
    if (it->second.token.empty()) {
      if (pos != std::string::npos) {
        return false;
      }
    } else {
      util::split(it->second.token, " ", token_result);
      std::size_t token_pos = 0;
      for (const auto& token : token_result) {
        auto n = log_line.find(token, token_pos);
        if (n == std::string::npos) {
          return false;
        }
        token_pos = n;
      }
      if (token_pos + 1 < log_line.size()) {
        if (std::string::npos != log_line.find('%', token_pos + 1)) {
          return false;
        }
      }
    }
  }
  file_path_ = file_path;
  language_ = language;
  return true;
}

auto kratos::lang::LangImpl::reload() -> bool {
  std::string file_path = file_path_;
  lang_map_.clear();
  return load(file_path);
}

} // namespace lang
} // namespace kratos
